package io.github.cchenxi.crpc.core.provider;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;

import org.springframework.util.MultiValueMap;

import io.github.cchenxi.crpc.core.api.RpcRequest;
import io.github.cchenxi.crpc.core.api.RpcResponse;
import io.github.cchenxi.crpc.core.meta.ProviderMeta;
import io.github.cchenxi.crpc.core.utils.TypeUtil;

/**
 * 服务提供调用
 * Date: 2024-03-22
 *
 * @author chenxi
 */
public class ProviderInvoker {
    private MultiValueMap<String, ProviderMeta> skeleton;

    public ProviderInvoker(ProviderBootstrap providerBootstrap) {
        this.skeleton = providerBootstrap.getSkeleton();
    }

    /**
     * 请求调用
     *
     * @param rpcRequest 请求体
     * @return 返回
     */
    public RpcResponse<Object> invoke(RpcRequest rpcRequest) {
        String methodSign = rpcRequest.getMethodSign();
        List<ProviderMeta> providerMetas = skeleton.get(rpcRequest.getService());

        RpcResponse<Object> rpcResponse = new RpcResponse<>();
        try {
            ProviderMeta providerMeta = findProviderMeta(providerMetas, methodSign);
            Method method = providerMeta.getMethod();
            Object[] args = processArgs(rpcRequest.getArgs(), method.getParameterTypes());
            Object result = method.invoke(providerMeta.getServiceImpl(), args);
            rpcResponse.setStatus(true);
            rpcResponse.setData(result);
        } catch (InvocationTargetException e) {
            rpcResponse.setStatus(false);
            rpcResponse.setEx(new RuntimeException(e.getTargetException().getMessage()));
        } catch (IllegalAccessException e) {
            rpcResponse.setStatus(false);
            rpcResponse.setEx(new RuntimeException(e.getMessage()));
        }
        return rpcResponse;
    }

    private Object[] processArgs(Object[] args, Class<?>[] parameterTypes) {
        if (args == null || args.length == 0) {
            return args;
        }

        Object[] actualArgs = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            actualArgs[i] = TypeUtil.cast(args[i], parameterTypes[i]);
        }

        return actualArgs;
    }

    private ProviderMeta findProviderMeta(List<ProviderMeta> providerMetas, String methodSign) {
        Optional<ProviderMeta> providerMeta = providerMetas.stream()
                .filter(meta -> methodSign.equals(meta.getMethodSign()))
                .findFirst();
        return providerMeta.orElse(null);
    }
}
